package com.ndp.fb.storage;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.*;
import com.ndp.fb.config.ConfigCentre;
import com.ndp.fb.constant.ConfigConst;
import com.ndp.fb.exception.CheckedException;
import com.ndp.fb.exception.ErrorCode;
import com.ndp.fb.exception.S3Exception;
import com.ndp.fb.util.StringUtil;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * manager static file storage,include
 * <p>
 * feed --xml
 * product---image
 * font
 * icon,logo,image
 * template
 * material style
 * <p>
 * Created by hunk.qin on 2016/4/15.
 */
@Component
public class S3ResourceManager {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private static final String SUFFIX = "/";
    private String bucketName = ConfigCentre.getString(ConfigConst.S3_RETENTION_RATE_BUCKET_NAME);

    private String s3TempDir = ConfigCentre.getString(ConfigConst.S3_RETENTION_RATE_TEMP_PATH);


    @Autowired
    private AmazonS3 amazonS3;

    public String save(String folderName, File file, boolean overWrite) throws IOException {
        String storepath = getFullFolder(folderName) + file;
        if (overWrite || !exist(storepath)) {
            save(folderName, file);
        }
        return storepath;
    }

    public String save(String folderName, String storeName, File file, boolean overWrite) throws IOException {
        String storepath = getFullFolder(folderName) + (StringUtil.isEmpty(storeName) ? file.getName() : storeName);
        if (overWrite || !exist(storepath)) {
            save(folderName, storeName, file);
        }
        return storepath;
    }

    public String saveForHttp(String folderName, String storeName, File file, boolean overWrite) throws IOException {
        String fullFolderName = getFullFolder(folderName);
        String storepath = fullFolderName + (StringUtil.isEmpty(storeName) ? file.getName() : storeName);
        if (overWrite || !exist(storepath)) {
            saveFile(file, fullFolderName, storepath, true);
        }
        return storepath;
    }

    public String saveForHttp(String folderName, File file, boolean overWrite) throws IOException {
        return saveForHttp(folderName, file.getName(), file, overWrite);
    }


    public String saveForPresignedUrl(String folderName, String storeName, File file, Date expirationDate) throws IOException {
        String fullFolderName = getFullFolder(folderName);
        String storepath = fullFolderName + (StringUtil.isEmpty(storeName) ? file.getName() : storeName);
        amazonS3.putObject(bucketName, storepath, new FileInputStream(file), new ObjectMetadata());
        GeneratePresignedUrlRequest urlRequest = new GeneratePresignedUrlRequest(bucketName, storepath);
        urlRequest.setExpiration(expirationDate);
        URL url = amazonS3.generatePresignedUrl(urlRequest);
        return url.toString();
    }


    public void deleteFolder(String folderName) {
        List<S3ObjectSummary> fileList = amazonS3.listObjects(bucketName, getFullFolder(folderName)).getObjectSummaries();
        for (S3ObjectSummary file : fileList) {
            amazonS3.deleteObject(bucketName, file.getKey());
        }
        amazonS3.deleteObject(bucketName, folderName);
    }

    public File getFile(String storePath) throws S3Exception {
        File tempdir = new File(s3TempDir);
        File file = new File(folderPolishing(s3TempDir) + FilenameUtils.getBaseName(storePath) + "_" + System.currentTimeMillis() + "." + FilenameUtils.getExtension(storePath));
        if (!tempdir.exists()) tempdir.mkdirs();
        if (tempdir.exists() && tempdir.isDirectory()) {
            if (file.exists()) {
                file.delete();
                logger.warn("get s3 file ,exist same name file in temp dir, file name is :" + file.getPath());
            }
            amazonS3.getObject(new GetObjectRequest(bucketName, storePath), file);
            return file;
        } else {
            logger.error(s3TempDir + " can not mkdirs,error------------------------");
            throw new S3Exception(ErrorCode.ERROR_80001, s3TempDir, "not exist");
        }
    }

    public void delete(String storePath) {
        amazonS3.deleteObject(bucketName, storePath);
    }

    public boolean exist(String checkpath) {
        boolean exist = true;
        try {
            ObjectMetadata object = amazonS3.getObjectMetadata(bucketName, checkpath);
        } catch (AmazonS3Exception e) {
            if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                exist = false;
            } else {
                logger.warn("check is exist error:" + e.getMessage(), e);
            }
        }
        return exist;
    }

    private String getStoreRoot() {
        String storeRoot = ConfigCentre.getString(ConfigConst.S3_STORE_ROOT);
        if (StringUtil.isEmpty(storeRoot)) return "test/";
        int length = storeRoot.length();
        return storeRoot.substring(length - 1, length).equals(SUFFIX) ? storeRoot : storeRoot + SUFFIX;
    }

    private String folderPolishing(String folder) {
        int length = folder.length();
        return folder.substring(length - 1, length).equals(SUFFIX) ? folder : folder + SUFFIX;
    }

    public String getFullFolder(String folder) {
        return getStoreRoot() + folderPolishing(folder);
    }

    private void saveFile(File file, String fullFolderName, String storepath, boolean httpAccess) throws FileNotFoundException {
        if (!exist(fullFolderName)) createFolder(fullFolderName);
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, storepath, file);
        if (httpAccess)
            putObjectRequest.withCannedAcl(CannedAccessControlList.PublicRead);
        amazonS3.putObject(putObjectRequest);
    }

    private void createFolder(String folderName) {
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(0);
        InputStream emptyContent = new ByteArrayInputStream(new byte[0]);
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, getFullFolder(folderName), emptyContent, metadata);
        amazonS3.putObject(putObjectRequest);
    }

    private String save(String folderName, File file) throws IOException {
        return save(folderName, file.getName(), file);
    }

    private String save(String folderName, String storeName, File file) throws IOException {
        String fullFolderName = getFullFolder(folderName);
        String storepath = fullFolderName + storeName;
        saveFile(file, fullFolderName, storepath, false);
        return storepath;
    }

    public String getS3TempDir() {
        return s3TempDir;
    }

    public String getBucketName() {
        return bucketName;
    }

    public void copyFile(String sourceKey, String descKey) {
        String fullFolderName = descKey.substring(0, descKey.lastIndexOf("/"));
        if (!exist(fullFolderName)) createFolder(fullFolderName);
        CopyObjectResult result = amazonS3.copyObject(bucketName,sourceKey,descKey,descKey);
    }
}
